#ifndef __M_DATA_H__
#define __M_DATA_H__

#include "conf.hpp"
#include "util.hpp"
#include <unordered_map>
#include <pthread.h>

namespace Cloud
{
    // 数据信息结构体
    struct BackedInfo
    {
        BackedInfo(const std::string &filepath = "")
            :pack_flag(false), fsize(0), atime(0), ctime(0), mtime(0)
        {
            // 根据 filepath 初始化成员信息
            FileUtil fu(filepath);

            if(fu.Exists() == false)
            {
                if(filepath != "")
                    std::cerr << "BackedInfo get file:[" << filepath << "]fail!" << std::endl;
                return;
            }
            
            fsize = fu.FileSize();
            atime = fu.LastAccTime();
            ctime = fu.LastCreTime();
            mtime = fu.LastModTime();
            real_path = filepath;

            Config* pc = Config::GetInstance();

            // 获取压缩文件必要信息（目录 + 后缀）
            std::string packDir = pc->GetPackFileDir();
            std::string packSuffix = pc->GetPackFileSuffix();
            pack_path = packDir + fu.FileName() + packSuffix;

            // 获取资源请求必要信息（前缀）
            url = pc->GetDownLoadPrefix() + fu.FileName();
        }

        bool pack_flag; // 是否被压缩标志
        size_t fsize; // 文件大小
        time_t atime; // 访问时间
        time_t ctime; // 创建时间
        time_t mtime; // 修改时间
        std::string real_path; // 文件实际存储路径
        std::string pack_path; // 压缩包存储路径
        std::string url; // 资源请求路径（下载路径）
    };

    // 数据管理类
    class DataManage
    {
    public:
        DataManage()
        {
            _backed_file = Config::GetInstance()->GetBackupFile(); // 获取持久化存储文件
            pthread_rwlock_init(&_rwlock, nullptr); // 初始化读写锁

            // 加载数据
            InitLoad();
        }

        ~DataManage()
        {
            pthread_rwlock_destroy(&_rwlock); // 销毁读写锁
        }

        // 插入数据
        bool Insert(const BackedInfo &info)
        {
            {
                // 写锁
                pthread_rwlock_wrlock(&_rwlock);
                _table[info.url] = info;
                pthread_rwlock_unlock(&_rwlock);
            }

            // 更新数据后持久化
            Storage();
            return true;
        }

        // 更新数据
        bool Update(const BackedInfo &info)
        {
            // 得益于 unordered_map 自带更新的特性（不允许重复），可以直接复用 Insert
            return Insert(info);
        }

        // 根据 URL 获取数据信息
        bool GetOneByURL(const std::string &url, BackedInfo *pinfo)
        {
            {
                // 读锁
                pthread_rwlock_rdlock(&_rwlock);
                auto it = _table.find(url);
                if(it == _table.end())
                {
                    std::cerr << "GetOneByURL fail!" << std::endl;
                    pthread_rwlock_unlock(&_rwlock);
                    return false;
                }

                *pinfo = it->second;
                pthread_rwlock_unlock(&_rwlock);
            }

            return true;
        }

        // 根据实际传入的路径获取数据
        bool GetOneByRealPath(const std::string &realpath, BackedInfo *pinfo)
        {
            {
                // 读锁
                pthread_rwlock_rdlock(&_rwlock);

                // 遍历 _table 中是否存在该路径
                for(auto &val : _table)
                {
                    if(val.second.real_path == realpath)
                    {
                        *pinfo = val.second;
                        pthread_rwlock_unlock(&_rwlock);
                        return true;
                    }
                }

                pthread_rwlock_unlock(&_rwlock);
            }

            return false;
        }

        // 遍历返回所有资源数据信息
        bool GetAll(std::vector<BackedInfo> *array)
        {
            {
                // 读锁
                pthread_rwlock_rdlock(&_rwlock);
                if(_table.empty())
                {
                    std::cerr << "Table is empty" << std::endl;
                    pthread_rwlock_unlock(&_rwlock);
                    return false;
                }

                for(auto &val : _table)
                    array->push_back(val.second);

                pthread_rwlock_unlock(&_rwlock);
            }

            return true;
        }

        // 持久化存储
        bool Storage()
        {
            Json::Value root;

            // 1.遍历 _table
            for(auto &it : _table)
            {
                // 2.新增 Json 子项
                Json::Value val;
                val["pack_flag"] = it.second.pack_flag;
                val["fsize"] = static_cast<Json::Int64>(it.second.fsize);
                val["atime"] = static_cast<Json::Int64>(it.second.atime);
                val["ctime"] = static_cast<Json::Int64>(it.second.ctime);
                val["mtime"] = static_cast<Json::Int64>(it.second.mtime);
                val["real_path"] = it.second.real_path;
                val["pack_path"] = it.second.pack_path;
                val["url"] = it.second.url;

                // 添加子项至 root
                root.append(val);
            }

            // 3.序列化
            std::string body;
            JsonUtil::Serialization(root, &body);

            // 4.写入持久化文件中
            FileUtil fu(_backed_file);
            fu.SetContent(body);

            return true;
        }

        // 初始化加载数据
        bool InitLoad()
        {
            // 1.读取 cloud.dat 中的数据
            FileUtil fu(_backed_file);
            if(fu.Exists() == false)
                return true;

            std::string body;
            fu.GetContent(&body);

            // 2.反序列化
            Json::Value root;
            JsonUtil::UnSerialization(body, &root);

            // 3.将 root 中的 Json 子项添加到 _table 中
            for(int i = 0; i < root.size(); i++)
            {
                BackedInfo info;
                info.pack_flag = root[i]["pack_flag"].asBool();
                info.fsize = static_cast<size_t>(root[i]["fsize"].asInt64());
                info.atime = static_cast<time_t>(root[i]["atime"].asInt64());
                info.ctime = static_cast<time_t>(root[i]["ctime"].asInt64());
                info.mtime = static_cast<time_t>(root[i]["mtime"].asInt64());
                info.real_path = root[i]["real_path"].asString();
                info.pack_path = root[i]["pack_path"].asString();
                info.url = root[i]["url"].asString();

                Insert(info);
            }

            return true;
        }

    private:
        std::string _backed_file; // 持久化存储文件名
        std::unordered_map<std::string, BackedInfo> _table; // 文件中存储映射关系
        pthread_rwlock_t _rwlock; // 读写锁（读共享、写互斥）
    };
}

#endif